package kode.boot.logging.autoconfigure;

import kode.boot.logging.Logging;
import kode.boot.logging.LoggingInfo;
import kode.boot.logging.LoggingResolver;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.UUID;

/**
 * DefaultLoggingResolver
 *
 * @author Stark
 * @date 2018/12/20
 */
public class DefaultLoggingResolver implements LoggingResolver {

	@Override
	public boolean support(Class<?> targetClass, Method method) {
		if (method != null) {
			Logging methodLogging = AnnotationUtils.findAnnotation(method, Logging.class);
			return methodLogging != null && methodLogging.enabled();
		}

		return false;
	}

	@Override
	public LoggingInfo resolve(MethodInvocation invocation) {
		var info = new LoggingInfo();

		var method = invocation.getMethod();
		var clazz = method.getDeclaringClass();

		if (clazz != null) {
			var logging = AnnotationUtils.findAnnotation(clazz, Logging.class);
			if (logging != null && logging.enabled()) {
				info.setLogBefore(logging.before());
				info.setLogAfter(logging.after());
				info.setDetails(logging.details());
			}
		}

		var logging = AnnotationUtils.findAnnotation(method, Logging.class);
		if (logging != null && logging.enabled()) {

			info.setLogBefore(LoggingUtil.joinWithoutEmpty(" - ", info.getLogBefore(), logging.before()));
			info.setLogAfter(LoggingUtil.joinWithoutEmpty(" - ", info.getLogAfter(), logging.after()));

			if (info.getDetails() == null) {
				info.setDetails(logging.details());
			} else {
				var newLen = info.getDetails().length + logging.details().length;
				var temp = Arrays.copyOf(info.getDetails(), newLen, String[].class);
				System.arraycopy(logging.details(), 0, temp, info.getDetails().length, logging.details().length);
				info.setDetails(temp);
			}
		}

		info.setId(UUID.randomUUID().toString());
		info.setRequestTime(System.currentTimeMillis());

		setFromReflect(info, invocation);
		setFromRequest(info);

		return info;
	}

	@Override
	public int getOrder() {
		return 0;
	}

	protected void setFromReflect(LoggingInfo info, MethodInvocation invocation) {
		var methodParams = new HashMap<String, Object>();
		var argsValues = invocation.getArguments();
		var method = invocation.getMethod();

		var clazz = method.getDeclaringClass();
		var methodArgs = method.getParameters();

		for (int i = 0, j = methodArgs.length; i < j; i++) {
			methodParams.put(methodArgs[i].getName(), argsValues[i]);
		}

		info.setMethodParameters(methodParams);
		info.setTarget(clazz);
		info.setMethod(method);
	}

	protected void setFromRequest(LoggingInfo info) {
		try {
			var request = LoggingUtil.getHttpServletRequest();
			if (null != request) {
				info.setHttpHeaders(LoggingUtil.getHeaders(request));
				info.setIp(LoggingUtil.getIpAddr(request));
				info.setHttpMethod(request.getMethod());
				info.setUrl(request.getRequestURL().toString());
				info.setPrincipal(request.getUserPrincipal());
			}
		} catch (Exception ignore) {
		}
	}
}
